﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Resources;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using Microsoft.Phone.Tasks;
using System.Threading.Tasks;
using ImageTools.Controls;
using ImageTools;

namespace TodayHumor
{
    public partial class Read : PhoneApplicationPage
    {
        bool HookedScrollEvents = false; // 중복으로 스크롤 이벤트가 걸리는 것을 방지하기 위해서
        Board board; // 읽을 게시판
        ArticleList articleList; // 게시글 목록
        PhoneTextBox CommentTB; // 댓글 TextBox

        #region ApplicationBar 버튼
        ApplicationBarIconButton WriteBtn = new ApplicationBarIconButton
        {
            IconUri = new Uri("Images/Icons/write.png", UriKind.Relative),
            Text = "글쓰기"
        };
        ApplicationBarIconButton SettingsBtn = new ApplicationBarIconButton
        {
            IconUri = new Uri("Images/Icons/settings.png", UriKind.Relative),
            Text = "설정"
        };
        ApplicationBarIconButton LikeBtn = new ApplicationBarIconButton
        {
            IconUri = new Uri("Images/Icons/ThumbsUp.png", UriKind.Relative),
            Text = "추천"
        };
        ApplicationBarIconButton DislikeBtn = new ApplicationBarIconButton
        {
            IconUri = new Uri("Images/Icons/ThumbsDown.png", UriKind.Relative),
            Text = "반대"
        };
        ApplicationBarIconButton CommentWriteBtn = new ApplicationBarIconButton
        {
            IconUri = new Uri("Images/Icons/write.png", UriKind.Relative),
            Text = "댓글 쓰기"
        };
        ApplicationBarIconButton CommentDeleteBtn = new ApplicationBarIconButton
        {
            IconUri = new Uri("Images/Icons/close.png", UriKind.Relative),
            Text = "지우기"
        };
        #endregion

        public Read()
        {
            InitializeComponent();

            Loaded += PageLoaded;

            WriteBtn.Click += new EventHandler(WriteButtonClick);
            SettingsBtn.Click += new EventHandler(SettingsBtn_Click);
            LikeBtn.Click += new EventHandler(LikeBtn_Click);
            DislikeBtn.Click += new EventHandler(DislikeBtn_Click);

            if (App.Current.LoginModule.Status != LoginModule.State.LoggedIn)
            {
                WriteBtn.IsEnabled = false;
            }

            if ((bool)PhoneApplicationService.Current.State["IsLightTheme"])
            {
                pivot.Background = new SolidColorBrush(Color.FromArgb(255, 228, 228, 228));
            }
        }

        async void PageLoaded(object sender, RoutedEventArgs e)
        {
            // 게시판 목록 선택 없이 글만 읽는다면
            if (PhoneApplicationService.Current.State.ContainsKey("ReadUri") && (bool)PhoneApplicationService.Current.State["ReadUri"])
            {
                Utilities.GeneratePI(this, "게시글을 로드하고 있습니다..");

                PhoneApplicationService.Current.State["ReadUri"] = false;
                pivot.Items.Remove(pivot.Items[0]);

                TitleTB.Text = "게시글 읽기";

                // 선택된 Article을 뽑아온다.
                Article article = (Article)PhoneApplicationService.Current.State["SelectedArticle"];

                // 선택된 Article이 null일 수도 있기 때문에 null이 아닌지 비교한다. (선택 후 SelectedItem = null을 해서)
                if (article != null)
                {
                    // ProgressIndicator를 띄우고
                    Utilities.GeneratePI(this, "글 내용을 다운로드하고 있습니다..");

                    // ArticleLoader를 새로 만든다.
                    ArticleLoader ArticleLoader = new ArticleLoader(article);

                    // 게시글을 로드한 뒤 lbArticleContent에 추가한다.
                    lbArticleContent.ItemsSource = await ArticleLoader.LoadArticle();
                    (Utilities.FindElement(lbArticleContent, typeof(ScrollViewer)) as ScrollViewer).ScrollToVerticalOffset(0);

                    // 열람 기록에 해당 Article을 추가한다.
                    App.Current.HistoryLoader.Add(article);

                    // ProgressIndicator를 숨긴다.
                    SystemTray.SetProgressIndicator(this, null);
                }
            }
            else
            {
                Utilities.GeneratePI(this, "게시글 목록을 로드하고 있습니다..");

                // 넘어오기 전 State에 저장된 선택된 게시판 정보를 저장하고, ArticleList를 새로 만든다.
                board = (Board)PhoneApplicationService.Current.State["SelectedBoard"];
                articleList = new ArticleList(board, 1);

                // TitleTB의 이름을 게시판 이름으로 바꾸고
                TitleTB.Text = board.Name;

                // 게시글 목록을 로드한 뒤 추가한다.
                List<Article> ArticleList = await articleList.LoadArticleList();

                foreach (Article article in ArticleList)
                {
                    lbArticleList.Items.Add(article);
                }

                SystemTray.SetProgressIndicator(this, null);
            }
        }

        #region Navigation 이벤트 핸들러
        protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs args)
        {
            if (pivot.SelectedIndex == 1)
            {
                pivot.SelectedIndex = 0; args.Cancel = true;
            }
            base.OnBackKeyPress(args);
        }
        #endregion

        #region 이벤트 핸들러
        private void PivotSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            switch (pivot.SelectedIndex)
            {
                case 0:
                    Utilities.SetApplicationBarButtons(ApplicationBar, WriteBtn, SettingsBtn);
                    break;
                case 1:
                    Utilities.SetApplicationBarButtons(ApplicationBar, LikeBtn, DislikeBtn);
                    break;
            }
        }

        private void LongListSelectorLoaded(object sender, RoutedEventArgs e)
        {
            if (HookedScrollEvents) { return; }
            HookedScrollEvents = true;
            ScrollViewer sc = (ScrollViewer)Utilities.FindElement(lbArticleList, typeof(ScrollViewer));

            FrameworkElement element = VisualTreeHelper.GetChild(sc, 0) as FrameworkElement;
            if (element != null)
            {
                VisualStateGroup vgroup = Utilities.FindVisualState(element, "VerticalCompression");
                if (vgroup != null)
                {
                    vgroup.CurrentStateChanging += new EventHandler<VisualStateChangedEventArgs>(ArticleListSelectorCompression);
                }
            }
        }

        private async void ArticleListSelectorCompression(object sender, VisualStateChangedEventArgs e)
        {
            if (e.NewState.Name == "CompressionBottom")
            {
                List<Article> ArticleList = await articleList.LoadArticleList();

                foreach (Article article in ArticleList)
                {
                    lbArticleList.Items.Add(article);
                }
            }
        }

        private void WriteButtonClick(object sender, EventArgs args)
        {
            PhoneApplicationService.Current.State["SelectedTable"] = board;

            NavigationService.Navigate(new Uri("/Page/Write.xaml", UriKind.Relative));
        }

        void SettingsBtn_Click(object sender, EventArgs args)
        {
            throw new NotImplementedException();
        }

        void LikeBtn_Click(object sender, EventArgs args)
        {
            throw new NotImplementedException();
        }

        void DislikeBtn_Click(object sender, EventArgs args)
        {
            throw new NotImplementedException();
        }

        private async void ArticleListSelectorSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // 선택된 Article을 뽑아온다.
            Article article = lbArticleList.SelectedItem as Article;

            // 선택된 Article이 null일 수도 있기 때문에 null이 아닌지 비교한다. (선택 후 SelectedItem = null을 해서)
            if (article != null)
            {
                // ProgressIndicator를 띄우고
                Utilities.GeneratePI(this, "글 내용을 다운로드하고 있습니다..");
                pivot.SelectedIndex = 1;

                // ArticleLoader를 새로 만든다.
                ArticleLoader ArticleLoader = new ArticleLoader(article);
                
                // 게시글을 로드한 뒤 lbArticleContent에 추가한다.
                lbArticleContent.ItemsSource = await ArticleLoader.LoadArticle();
                (Utilities.FindElement(lbArticleContent, typeof(ScrollViewer)) as ScrollViewer).ScrollToVerticalOffset(0);

                // 열람 기록에 해당 Article을 추가하고 열람 기록을 저장한다.
                App.Current.HistoryLoader.Add(article);
                App.Current.HistoryLoader.Save();

                // ProgressIndicator를 숨긴다.
                SystemTray.SetProgressIndicator(this, null);
            }
            lbArticleList.SelectedItem = null;
        }

        private void CommentTextBoxLoaded(object sender, RoutedEventArgs e)
        {
            CommentTB = (sender as PhoneTextBox);

            if (App.Current.LoginModule.Status != LoginModule.State.LoggedIn)
            {
                CommentTB.Text = "로그인 후 댓글을 달 수 있습니다.";
                CommentTB.IsEnabled = false;
            }
        }

        private void CommentTextBoxGotFocus(object sender, RoutedEventArgs e)
        {
            Utilities.SetApplicationBarButtons(ApplicationBar, CommentWriteBtn, CommentDeleteBtn);
        }

        private void CommentTextBoxLostFocus(object sender, RoutedEventArgs e)
        {
            Utilities.SetApplicationBarButtons(ApplicationBar, LikeBtn, DislikeBtn, WriteBtn);
        }

        private void CommentTextBoxTextChanged(object sender, TextChangedEventArgs e)
        {
            if ((CommentTB != null) && (!string.IsNullOrEmpty(CommentTB.Text)))
            {
                Brush foregroundBrush = (Brush)Application.Current.Resources["PhoneTextBoxForegroundBrush"];
                if (CommentTB.Foreground != foregroundBrush)
                {
                    CommentTB.Foreground = foregroundBrush;
                }
            }
        }

        private async void ArticlePartGridLoaded(object sender, RoutedEventArgs e)
        {
            // ArticlePart를 받아와서
            ArticlePart ArticlePart = (sender as Grid).DataContext as ArticlePart;

            try
            {
                if (ArticlePart.ImageLoaded) { return; }
                Uri PicUri = ArticlePart.PicUri;

                // HttpWebResponse의 Stream을 받아온 뒤
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(PicUri);
                req.Headers["Accept-Language"] = "ko-KR,ko;q=0.8,en-US;q=0.6,en;q=0.4";
                req.CookieContainer = App.Cookies;

                HttpWebResponse res = (HttpWebResponse)await req.GetResponseAsync();

                await Dispatcher.InvokeAsync(() =>
                {
                    if (res.ContentType.Equals("image/gif", StringComparison.CurrentCultureIgnoreCase))
                    {
                        ExtendedImage source = new ExtendedImage();
                        source.SetSource(res.GetResponseStream());

                        AnimatedImage image = new AnimatedImage
                        {
                            Margin = new Thickness(0, 10, 0, 10),
                            Source = source
                        };

                        image.LoadingFailed += (FailedImage, args) =>
                        {
                            StackPanel AlertStackPanel = ((FailedImage as AnimatedImage).Parent as Grid).Children[0] as StackPanel;
                            (AlertStackPanel.Children[0] as TextBlock).Text = "이미지를 불러올 수 없었습니다";
                            (AlertStackPanel.Children[1] as TextBlock).Text = "지원하지 않는 이미지 포맷이거나 다운로드 과정에서 문제가 발생했습니다.";
                        };

                        (sender as Grid).Children.Add(image);
                    }
                    else
                    {
                        BitmapImage source = new BitmapImage();
                        source.SetSource(res.GetResponseStream());

                        Image image = new Image
                        {
                            Margin = new Thickness(0, 10, 0, 10),
                            Source = source
                        };

                        image.ImageFailed += (FailedImage, args) =>
                        {
                            StackPanel AlertStackPanel = ((FailedImage as Image).Parent as Grid).Children[0] as StackPanel;
                            (AlertStackPanel.Children[0] as TextBlock).Text = "이미지를 불러올 수 없었습니다";
                            (AlertStackPanel.Children[1] as TextBlock).Text = "지원하지 않는 이미지 포맷이거나 다운로드 과정에서 문제가 발생했습니다.";
                        };

                        (sender as Grid).Children.Add(image);
                    }
                });
            }
            catch
            {
                StackPanel AlertStackPanel = (sender as Grid).Children[0] as StackPanel;
                (AlertStackPanel.Children[0] as TextBlock).Text = "이미지를 불러올 수 없었습니다";
                (AlertStackPanel.Children[1] as TextBlock).Text = "지원하지 않는 이미지 포맷이거나 다운로드 과정에서 문제가 발생했습니다.";
            }

            ArticlePart.ImageLoaded = true;
        }

        private void ContentImageTap(object sender, Microsoft.Phone.Controls.GestureEventArgs e)
        {
            ArticlePart ImagePart = (sender as Grid).DataContext as ArticlePart;
            WebBrowserTask ImageTask = new WebBrowserTask
            {
                Uri = ImagePart.PicUri
            }; ImageTask.Show();
        }
        #endregion

        #region ViewModel 이벤트 핸들러
        void ViewModel_ArticleListLoadStarted(object sender, EventArgs e)
        {
            Utilities.GeneratePI(this, "게시글 목록을 불러오고 있습니다.");
        }

        void ViewModel_DataLoadFinished(object sender, EventArgs e)
        {
            Deployment.Current.Dispatcher.BeginInvoke(delegate
            {
                Microsoft.Phone.Shell.SystemTray.SetProgressIndicator(this, null);
            });
        }
        #endregion

        private void ContentImageOpened(object sender, RoutedEventArgs e)
        {
        }
    }

    public abstract class DataTemplateSelector : ContentControl
    {
        public virtual DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            return null;
        }

        protected override void OnContentChanged(object oldContent, object newContent)
        {
            base.OnContentChanged(oldContent, newContent);

            ContentTemplate = SelectTemplate(newContent, this);
        }
    }

    public class ArticleTemplateSelector : DataTemplateSelector
    {
        public DataTemplate Title { get; set; }
        public DataTemplate ArticleContent { get; set; }
        public DataTemplate Link { get; set; }
        public DataTemplate AnimatedImage { get; set; }
        public DataTemplate Image { get; set; }
        public DataTemplate Comment { get; set; }
        public DataTemplate Write { get; set; }
        public DataTemplate CommentsAndDate { get; set; }
        public DataTemplate CommentContent { get; set; }

        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            ArticlePart ArticleItem = item as ArticlePart;
            if (ArticleItem != null)
            {
                switch (ArticleItem.ArticlePartType)
                {
                    case ArticlePart.Type.Title: return Title;
                    case ArticlePart.Type.Content: return ArticleContent;
                    case ArticlePart.Type.Link: return Link;
                    case ArticlePart.Type.Image: return Image;
                    case ArticlePart.Type.AnimatedImage: return AnimatedImage;
                    case ArticlePart.Type.Comment: return Comment;
                    case ArticlePart.Type.CommentContent: return CommentContent;
                    case ArticlePart.Type.Write: return Write;
                    case ArticlePart.Type.CommentsAndDate: return CommentsAndDate;
                }
            }
            return base.SelectTemplate(item, container);
        }
    }
}